﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Net;
using System.Web;
using System.Web.Script.Serialization;
using System.Data.SQLite;

namespace BugServer
{
    class Program
    {
        static SQLiteConnection cn;
        static SQLiteCommand cmd;
        static Dictionary<string, string> paramDict = new Dictionary<string, string>();
        const string dbFile = "data.db";

        static string title;
        static int port = 8081;
        static HttpListener httpListener;

        static Dictionary<string, Project> projectDict = new Dictionary<string, Project>();
        static Dictionary<string, User> userDict = new Dictionary<string, User>();

        static Dictionary<string, string> userTokenDict = new Dictionary<string, string>();
        static Dictionary<string, string> tokenUserDict = new Dictionary<string, string>();
        static int maxBugId;

        const string htmlPath = "html";
        const string ipFile = "ip.txt";
        static string ip = string.Empty;

        static void Main(string[] args)
        {
            if (!File.Exists(dbFile))
                CreateDb();
            else
            {
                InitDb();
                cmd.CommandText = "select ID from Bug order by ID desc limit 1";
                var reader = cmd.ExecuteReader();
                if (reader.Read())
                    maxBugId = reader.GetInt32(0);
                reader.Close();
            }
            ReadXml();

            if (File.Exists(ipFile))
                ip = File.ReadAllText(ipFile);

            try
            {
                httpListener = new HttpListener();
                httpListener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
                httpListener.Prefixes.Add("http://*:" + port + "/");
                httpListener.Start();
                ConsoleColor color = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("服务已启动，监听端口" + port);
                Console.ForegroundColor = color;
            }
            catch (Exception e)
            {
                LogError("监听端口时发生错误：" + e.Message);
                System.Threading.Thread.Sleep(3000);
                return;
            }
            while (true)
            {
                try
                {
                    HttpListenerContext context = httpListener.GetContext();
                    HttpListenerRequest request = context.Request;
                    HttpListenerResponse response = context.Response;
                    if (request.HttpMethod == "GET" || request.HttpMethod == "POST")
                    {
                        byte[] buffer;
                        if (request.RawUrl.EndsWith(".ico"))
                        {
                            string url = "html" + request.RawUrl;
                            if (File.Exists(url))
                                buffer = File.ReadAllBytes(url);
                            else
                                buffer = new byte[] { };
                        }
                        else
                        {
                            string reply = OnGetMessage(request);
                            if(request.RawUrl.Contains("utf8") || request.RawUrl.Length < 2 || request.RawUrl.EndsWith(".html"))
                                buffer = Encoding.UTF8.GetBytes(reply);
                            else
                                buffer = Encoding.GetEncoding("GBK").GetBytes(reply);
                        }
                        response.OutputStream.Write(buffer, 0, buffer.Length);
                    }
                    response.Close();
                }
                catch (Exception ex)
                {
                    LogError(ex.ToString());
                }
            }
        }
        static void Log(string log)
        {
            ConsoleColor color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(DateTime.Now.ToString("MM/dd HH:mm ") + log);
            Console.ForegroundColor = color;
        }
        static void LogError(string error)
        {
            ConsoleColor color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(DateTime.Now.ToString("MM/dd HH:mm ") + error);
            Console.ForegroundColor = color;
        }

        static void InitDb()
        {
            cn = new SQLiteConnection("Data Source=" + dbFile);
            cn.Open();
            cmd = new SQLiteCommand();
            cmd.Connection = cn;
        }

        static void CreateDb()
        {
            SQLiteConnection.CreateFile(dbFile);
            InitDb();
            
            StringBuilder cmdBuilder = new StringBuilder();
            cmdBuilder.AppendFormat("ID UNSIGNED INT NOT NULL");
            cmdBuilder.AppendFormat(",Project TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Title TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Content TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Priority UNSIGNED TINYINT NOT NULL");
            cmdBuilder.AppendFormat(",State UNSIGNED TINYINT NOT NULL");
            cmdBuilder.AppendFormat(",CreateTime DATETIME NOT NULL");
            cmdBuilder.AppendFormat(",Creater TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Assignee TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Resolver TEXT NOT NULL");
            cmd.CommandText = string.Format("create table Bug ({0})", cmdBuilder.ToString());
            cmd.ExecuteNonQuery();

            cmdBuilder.Clear();
            cmdBuilder.AppendFormat("Time DATETIME NOT NULL");
            cmdBuilder.AppendFormat(",Project TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Bug UNSIGNED INT NOT NULL");
            cmdBuilder.AppendFormat(",Type UNSIGNED TINYINT NOT NULL");
            cmdBuilder.AppendFormat(",User TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Assignee TEXT NOT NULL");
            cmdBuilder.AppendFormat(",Comment TEXT NOT NULL");
            cmd.CommandText = string.Format("create table History ({0})", cmdBuilder.ToString());
            cmd.ExecuteNonQuery();
        }

        static void ReadXml()
        {
            XmlDocument xml = new XmlDocument();
            xml.Load("config.xml");
            XmlNode node = xml.SelectSingleNode("config");
            XmlElement config = (XmlElement)node;
            title = config.GetAttribute("title");
            Console.Title = title;
            int.TryParse(config.GetAttribute("port"), out port);
            XmlNodeList nodeList = node.ChildNodes;
            for (int i = 0; i < nodeList.Count; i++)
            {
                if (nodeList[i].Name.Equals("user"))
                {
                    XmlElement xmlele = (XmlElement)nodeList[i];
                    User user = new User();
                    user.id = xmlele.GetAttribute("id");
                    user.name = xmlele.GetAttribute("name");
                    user.password = xmlele.GetAttribute("password");
                    userDict[user.id] = user;
                }
                else if (nodeList[i].Name.Equals("project"))
                {
                    XmlElement xmlele = (XmlElement)nodeList[i];
                    Project project = new Project();
                    project.id = xmlele.GetAttribute("id");
                    project.name = xmlele.GetAttribute("name");
                    project.userlist.AddRange(xmlele.GetAttribute("users").Split(','));
                    projectDict[project.id] = project;
                }
            }
        }

        static string GetNowDateTimeStr()
        {
            return DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
        }

        static void HandleStr(ref string str)
        {
            str = str.Replace("'", "''");
        }

        static string OnGetMessage(HttpListenerRequest request)
        {
            string url = request.RawUrl;
            if (url.Length < 2)
                url = "/login.html";
            if (!url.StartsWith("/"))
                return string.Empty;
            if (url.EndsWith(".html"))
            {
                url = htmlPath + url;
                if (File.Exists(url))
                    return File.ReadAllText(url);
                return string.Empty;
            }
            url = url.Remove(0, 1);
            paramDict.Clear();
            string[] info = url.Split('?');
            string func = info[0];
            if (info.Length == 2)
            {
                string[] paramArray = info[1].Split('&');
                foreach (string param in paramArray)
                {
                    string[] pInfo = param.Split('=');
                    if (pInfo.Length == 2)
                        paramDict[pInfo[0]] = HttpUtility.UrlDecode(pInfo[1]);
                }
            }
            if (request.HttpMethod == "POST")
            {
                StreamReader sr = new StreamReader(request.InputStream);
                string data = sr.ReadToEnd();
                sr.Close();
                string[] paramArray = data.Split('&');
                foreach (string param in paramArray)
                {
                    string[] pInfo = param.Split('=');
                    if (pInfo.Length == 2)
                        paramDict[pInfo[0]] = HttpUtility.UrlDecode(pInfo[1]);
                }
            }
            switch (func)
            {
                case "gethost":
                    return ip;
                case "posthost":
                    {
                        string ip;
                        if (!paramDict.TryGetValue("ip", out ip))
                            ip = request.RemoteEndPoint.Address.ToString();
                        return OnPostHost(ip);
                    }
                case "login":
                    return OnLogin();
                case "create":
                    return OnCreateBug();
                case "edit":
                    return OnEditBug();
                case "bug":
                    return OnGetBugList();
                case "view":
                    return OnViewBug();
                case "dynamic":
                    return OnDynamic();
                case "assign":
                    return OnAssignBug();
                case "activate":
                    return OnActivateBug();
                case "resolve":
                    return OnResolveBug();
                case "close":
                    return OnCloseBug();
            }
            return string.Empty;
        }

        static string OnPostHost(string _ip)
        {
            if (ip != _ip)
            {
                Log("IP变化 " + ip + " >> " + _ip);
                ip = _ip;
                File.WriteAllText(ipFile, ip);
            }
            return ip;
        }

        static string OnLogin()
        {
            string id, password;
            if (!paramDict.TryGetValue("id", out id)
            || !paramDict.TryGetValue("password", out password))
                return string.Empty;
            if (!userDict.ContainsKey(id) || !userDict[id].password.Equals(password))
                return string.Empty;
            string token;
            if (userTokenDict.ContainsKey(id))
                token = userTokenDict[id];
            else
            {
                byte[] r = new byte[8];
                Random rand = new Random((int)(DateTime.Now.Ticks % 1000000));
                int ran;
                for (int i = 0; i < 8; i++)
                    do
                    {
                        ran = rand.Next(48, 122);
                        r[i] = Convert.ToByte(ran);
                    } while ((ran >= 58 && ran <= 64) || (ran >= 91 && ran <= 96));

                token = Encoding.ASCII.GetString(r);
            }

            userTokenDict[id] = token;
            tokenUserDict[token] = id;

            JsMain maininfo = new JsMain();
            maininfo.token = token;
            maininfo.title = title + " - " + userDict[id].name;

            List<JsUser> users = new List<JsUser>();
            foreach (var kv in userDict)
            {
                JsUser user = new JsUser();
                user.id = kv.Key;
                user.name = kv.Value.name;
                users.Add(user);
            }
            maininfo.users = users.ToArray();

            List<JsProject> projects = new List<JsProject>();
            foreach (var p in projectDict)
            {
                if (!p.Value.userlist.Contains(id))
                    continue;
                JsProject project = new JsProject();
                project.id = p.Key;
                project.name = p.Value.name;
                List<string> pusers = new List<string>();
                foreach (var user in p.Value.userlist)
                {
                    pusers.Add(user);
                }
                project.users = pusers.ToArray();
                projects.Add(project);
            }
            maininfo.projects = projects.ToArray();
            JavaScriptSerializer js = new JavaScriptSerializer();
            return js.Serialize(maininfo);
        }

        static string OnCreateBug()
        {
            string token, project, assign, title, content, priorityStr;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("project", out project)
            || !paramDict.TryGetValue("assign", out assign)
            || !paramDict.TryGetValue("title", out title)
            || !paramDict.TryGetValue("priority", out priorityStr))
                return string.Empty;
            HandleStr(ref title);
            if (paramDict.TryGetValue("content", out content))
                HandleStr(ref content);
            else
                content = string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (!projectDict.ContainsKey(project))
                return string.Empty;

            if (!projectDict[project].userlist.Contains(tokenUserDict[token]))
                return string.Empty;

            if (!userDict.ContainsKey(assign))
                return string.Empty;
            
            byte.TryParse(priorityStr, out byte priority);

            cmd.CommandText = string.Format("insert into Bug values ({0}, '{1}', '{2}', '{3}', {4}, {5}, '{6}', '{7}', '{8}', '{9}')",
                ++maxBugId, project, title, content, priority, (byte)eBugState.Active, GetNowDateTimeStr(), tokenUserDict[token], assign, "");
            cmd.ExecuteNonQuery();

            cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                GetNowDateTimeStr(), project, maxBugId, (byte)eOpType.Create, tokenUserDict[token], assign, priority);
            cmd.ExecuteNonQuery();

            return "ok";
        }

        static string OnEditBug()
        {
            string token, bugStr, title, content, comment, priorityStr;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr)
            || !paramDict.TryGetValue("title", out title))
                return string.Empty;
            HandleStr(ref title);
            if (paramDict.TryGetValue("content", out content))
                HandleStr(ref content);
            else
                content = string.Empty;
            if (paramDict.TryGetValue("comment", out comment))
                HandleStr(ref comment);
            else
                comment = string.Empty;

            if (!paramDict.TryGetValue("priority", out priorityStr))
                priorityStr = string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);

            if (bugId == 0)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (string.Empty != title)
            {
                cmd.CommandText = string.Format("update Bug set Title = '{0}' where ID = {1}", title, bugId);
                cmd.ExecuteNonQuery();
            }
            if (string.Empty != content)
            {
                cmd.CommandText = string.Format("update Bug set Content = '{0}' where ID = {1}", content, bugId);
                cmd.ExecuteNonQuery();
            }
            if (string.Empty != priorityStr)
            {
                byte.TryParse(priorityStr, out byte priority);
                cmd.CommandText = string.Format("update Bug set Priority = {0} where ID = {1}", priority, bugId);
                cmd.ExecuteNonQuery();
            }
            string project = string.Empty;
            if (string.Empty != title || string.Empty != content || string.Empty != priorityStr)
            {
                cmd.CommandText = "select Project from Bug where ID = " + bugId;
                var reader = cmd.ExecuteReader();
                if (reader.Read())
                    project = reader.GetString(0);
                reader.Close();

                cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                    GetNowDateTimeStr(), project, bugId, (byte)eOpType.Edit, tokenUserDict[token], priorityStr, comment);
                cmd.ExecuteNonQuery();
            }
            else if (string.Empty != comment)
            {
                if (string.Empty == project)
                {
                    cmd.CommandText = "select Project from Bug where ID = " + bugId;
                    var reader = cmd.ExecuteReader();
                    if (reader.Read())
                        project = reader.GetString(0);
                    reader.Close();
                }
                cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                    GetNowDateTimeStr(), project, bugId, (byte)eOpType.Comment, tokenUserDict[token], priorityStr, comment);
                cmd.ExecuteNonQuery();
            }

            return "ok";
        }

        static string OnGetBugList()
        {
            string token, project, typeStr, pageStr, searchStr;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("project", out project)
            || !paramDict.TryGetValue("type", out typeStr)
            || !paramDict.TryGetValue("page", out pageStr))
                return string.Empty;
            if (paramDict.TryGetValue("search", out searchStr))
                HandleStr(ref searchStr);
            else
                searchStr = string.Empty;

            byte type = 0;
            int page = 1;
            byte.TryParse(typeStr, out type);
            int.TryParse(pageStr, out page);

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (!projectDict.ContainsKey(project))
                return string.Empty;

            if (!projectDict[project].userlist.Contains(tokenUserDict[token]))
                return string.Empty;

            string whereStr, orderByStr;
            switch ((eListType)type)
            {
                case eListType.AssignToMe:
                    whereStr = string.Format("Project = '{0}' and Assignee = '{1}'", project, tokenUserDict[token]);
                    orderByStr = "Priority desc,ID desc";
                    break;
                case eListType.CreateByMe:
                    whereStr = string.Format("Project = '{0}' and Creater = '{1}'", project, tokenUserDict[token]);
                    orderByStr = "ID desc";
                    break;
                case eListType.ResolveByMe:
                    whereStr = string.Format("Project = '{0}' and Resolver = '{1}'", project, tokenUserDict[token]);
                    orderByStr = "ID desc";
                    break;
                case eListType.UnResolved:
                    whereStr = string.Format("Project = '{0}' and State = 1", project);
                    orderByStr = "Priority desc,ID desc";
                    break;
                case eListType.ToClosed:
                    whereStr = string.Format("Project = '{0}' and State > 1", project);
                    orderByStr = "Priority desc,ID desc";
                    break;
                case eListType.UnClosed:
                    whereStr = string.Format("Project = '{0}' and State > 0", project);
                    orderByStr = "Priority desc,ID desc";
                    break;
                case eListType.All:
                    whereStr = string.Format("Project = '{0}'", project);
                    orderByStr = "ID desc";
                    break;
                default:
                    return string.Empty;
            }
            if (!string.IsNullOrWhiteSpace(searchStr))
                whereStr += string.Format(" and (Title like '%{0}%' or Content like '%{0}%')", searchStr);
            int totalCount = 0;
            cmd.CommandText = "select count(*) from Bug where " + whereStr;
            var reader = cmd.ExecuteReader();
            if (reader.Read())
                totalCount = reader.GetInt32(0);
            reader.Close();

            JsBugs jbugs = new JsBugs();
            jbugs.total = (totalCount - 1) / 20 + 1;
            if (page > jbugs.total)
                page = jbugs.total;
            jbugs.page = page;

            List<JsBug> bugs = new List<JsBug>();
            cmd.CommandText = string.Format("select * from Bug where {0} order by {1} limit 20 offset {2}", whereStr, orderByStr, (page - 1) * 20);
            reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                JsBug bug = new JsBug();
                bugs.Add(bug);
                bug.id = reader.GetInt32(0).ToString();
                bug.title = reader.GetString(2);
                bug.content = reader.GetString(3);
                bug.priority = reader.GetByte(4);
                bug.state = reader.GetByte(5);
                bug.time = reader.GetString(6);
                bug.creater = reader.GetString(7);
                bug.assignee = reader.GetString(8);
                bug.resolver = reader.GetString(9);
            }
            reader.Close();
            jbugs.bugs = bugs.ToArray();
            JavaScriptSerializer js = new JavaScriptSerializer();
            return js.Serialize(jbugs);
        }

        static string OnAssignBug()
        {
            string token, bugStr, assign, comment;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr)
            || !paramDict.TryGetValue("assign", out assign))
                return string.Empty;
            if (paramDict.TryGetValue("comment", out comment))
                HandleStr(ref comment);
            else
                comment = string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);

            if (bugId == 0)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (!userDict.ContainsKey(assign))
                return string.Empty;

            string project = string.Empty;
            byte state = 0;
            cmd.CommandText = "select Project, State from Bug where ID = " + bugId;
            var reader = cmd.ExecuteReader();
            if (reader.Read())
            {
                project = reader.GetString(0);
                state = reader.GetByte(1);
            }
            reader.Close();

            if (state == (byte)eBugState.Closed)
                return string.Empty;

            cmd.CommandText = string.Format("update Bug set Assignee = '{0}' where ID = {1}", assign, bugId);
            cmd.ExecuteNonQuery();
            cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                GetNowDateTimeStr(), project, bugId, (byte)eOpType.Assign, tokenUserDict[token], assign, comment);
            cmd.ExecuteNonQuery();

            return "ok";
        }

        static string OnActivateBug()
        {
            string token, bugStr, assign, comment;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr)
            || !paramDict.TryGetValue("assign", out assign))
                return string.Empty;
            if (paramDict.TryGetValue("comment", out comment))
                HandleStr(ref comment);
            else
                comment = string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);

            if (bugId == 0)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (!userDict.ContainsKey(assign))
                return string.Empty;

            string project = string.Empty;
            byte state = 0;
            cmd.CommandText = "select Project, State from Bug where ID = " + bugId;
            var reader = cmd.ExecuteReader();
            if (reader.Read())
            {
                project = reader.GetString(0);
                state = reader.GetByte(1);
            }
            reader.Close();

            if (state == (byte)eBugState.Active)
                return string.Empty;

            cmd.CommandText = string.Format("update Bug set Assignee = '{0}', State = 1 where ID = {1}", assign, bugId);
            cmd.ExecuteNonQuery();
            cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                GetNowDateTimeStr(), project, bugId, (byte)eOpType.Activate, tokenUserDict[token], assign, comment);
            cmd.ExecuteNonQuery();

            return "ok";
        }

        static string OnResolveBug()
        {
            string token, bugStr, typeStr, assign, comment;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr)
            || !paramDict.TryGetValue("assign", out assign)
            || !paramDict.TryGetValue("type", out typeStr))
                return string.Empty;
            if (paramDict.TryGetValue("comment", out comment))
                HandleStr(ref comment);
            else
                comment = string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);
            if (bugId == 0)
                return string.Empty;

            byte type = 0;
            byte.TryParse(typeStr, out type);
            if (type <= (byte)eBugState.Active || type > (byte)eBugState.NotFix)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            if (!userDict.ContainsKey(assign))
                return string.Empty;

            string project = string.Empty;
            byte state = 0;
            cmd.CommandText = "select Project, State from Bug where ID = " + bugId;
            var reader = cmd.ExecuteReader();
            if (reader.Read())
            {
                project = reader.GetString(0);
                state = reader.GetByte(1);
            }
            reader.Close();

            if (state != (byte)eBugState.Active)
                return string.Empty;

            cmd.CommandText = string.Format("update Bug set Assignee = '{0}', State = {1}, Resolver = '{2}' where ID = {3}", assign, type, tokenUserDict[token], bugId);
            cmd.ExecuteNonQuery();

            cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                GetNowDateTimeStr(), project, bugId, (byte)eOpType.ResolveByFixed + type - 2, tokenUserDict[token], assign, comment);
            cmd.ExecuteNonQuery();

            return "ok";
        }

        static string OnCloseBug()
        {
            string token, bugStr, comment;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr))
                return string.Empty;
            if (paramDict.TryGetValue("comment", out comment))
                HandleStr(ref comment);
            else
                comment = string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);
            if (bugId == 0)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            string project = string.Empty;
            byte state = 0;
            cmd.CommandText = "select Project, State from Bug where ID = " + bugId;
            var reader = cmd.ExecuteReader();
            if (reader.Read())
            {
                project = reader.GetString(0);
                state = reader.GetByte(1);
            }
            reader.Close();

            if (state <= (byte)eBugState.Active)
                return string.Empty;

            cmd.CommandText = string.Format("update Bug set Assignee = '', State = 0 where ID = {0}", bugId);
            cmd.ExecuteNonQuery();

            cmd.CommandText = string.Format("insert into History values ('{0}', '{1}', {2}, {3}, '{4}', '{5}', '{6}')",
                GetNowDateTimeStr(), project, bugId, (byte)eOpType.Close, tokenUserDict[token], "", comment);
            cmd.ExecuteNonQuery();

            return "ok";
        }

        static string OnViewBug()
        {
            string token, bugStr;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("bug", out bugStr))
                return string.Empty;

            int bugId = 0;
            int.TryParse(bugStr, out bugId);
            if (bugId == 0)
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            JsHistorys jhistorys = new JsHistorys();
            List<JsHistory> historys = new List<JsHistory>();
            cmd.CommandText = "select Time, Type, User, Assignee, Comment from History where Bug = " + bugId + " order by Time desc";
            var reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                JsHistory history = new JsHistory();
                historys.Add(history);
                history.time = reader.GetString(0);
                history.type = reader.GetByte(1);
                history.user = reader.GetString(2);
                history.assignee = reader.GetString(3);
                history.comment_title = reader.GetString(4);
            }
            reader.Close();
            jhistorys.historys = historys.ToArray();
            JavaScriptSerializer js = new JavaScriptSerializer();
            return js.Serialize(jhistorys);
        }

        static string OnDynamic()
        {
            string token, project;
            if (!paramDict.TryGetValue("token", out token)
            || !paramDict.TryGetValue("project", out project))
                return string.Empty;

            if (!projectDict.ContainsKey(project))
                return string.Empty;

            if (!tokenUserDict.ContainsKey(token))
                return string.Empty;

            JsHistorys jhistorys = new JsHistorys();
            List<JsHistory> historys = new List<JsHistory>();
            cmd.CommandText = "select Time, Bug, Type, User, Assignee, Comment from History where Project = '" + project + "' order by Time desc limit 50";
            var reader = cmd.ExecuteReader();
            Dictionary<JsHistory, int> dict = new Dictionary<JsHistory, int>();
            while (reader.Read())
            {
                JsHistory history = new JsHistory();
                historys.Add(history);
                history.time = reader.GetString(0);
                dict[history] = reader.GetInt32(1);
                history.type = reader.GetByte(2);
                history.user = reader.GetString(3);
                history.assignee = reader.GetString(4);
                string comment = reader.GetString(5);
            }
            reader.Close();

            foreach (var p in dict)
            {
                cmd.CommandText = "select Title from Bug where ID = " + p.Value;
                reader = cmd.ExecuteReader();
                string title = string.Empty;
                if (reader.Read())
                    title = reader.GetString(0);
                p.Key.comment_title = title;
                reader.Close();
            }
            jhistorys.historys = historys.ToArray();
            JavaScriptSerializer js = new JavaScriptSerializer();
            return js.Serialize(jhistorys);
        }
        
        class User
        {
            public string id;
            public string name;
            public string password;
        }

        class Project
        {
            public string id;
            public string name;
            public List<string> userlist = new List<string>();
        }

        enum eListType
        {
            AssignToMe,
            CreateByMe,
            ResolveByMe,
            UnResolved,
            ToClosed,
            UnClosed,
            All
        }

        enum eBugState
        {
            Closed,
            Active,
            Fixed,
            ByDesign,
            NotRepro,
            NotFix,
        }
    }
}
